﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Reflection;
using System.Web;
using System.Web.UI;

namespace Exhibition.Common.Web
{

    public class UserControlRenderingHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            string fileType = context.Request.FilePath;
            string appRelativePath = context.Request.AppRelativeCurrentExecutionFilePath;
            string controlPath = appRelativePath.ToLower().Replace(".uc", ".ascx");

            ViewManager<UserControl> viewManager = new ViewManager<UserControl>();
            UserControl control = viewManager.LoadViewControl(controlPath);

            SetPropertyValues(control, context);

            context.Response.ContentType = "text/html";
            context.Response.Write(viewManager.RenderView(control));
        }

        private static Dictionary<
        Type,
        Dictionary<
            PropertyInfo,
            List<UserControlRenderingPropertyAttribute>>> s_metadataCache =
        new Dictionary<
            Type,
            Dictionary<
                PropertyInfo,
                List<UserControlRenderingPropertyAttribute>>>();
        private static Dictionary<PropertyInfo, object> s_defaultValueCache =
            new Dictionary<PropertyInfo, object>();
        private static object s_mutex = new object();

        private static Dictionary<
            PropertyInfo,
            List<UserControlRenderingPropertyAttribute>> GetMetadata(Type type)
        {
            if (!s_metadataCache.ContainsKey(type))
            {
                lock (s_mutex)
                {
                    if (!s_metadataCache.ContainsKey(type))
                    {
                        s_metadataCache[type] = LoadMetadata(type);
                    }
                }
            }

            return s_metadataCache[type];
        }

        private static Dictionary<
            PropertyInfo,
            List<UserControlRenderingPropertyAttribute>> LoadMetadata(Type type)
        {
            Dictionary<PropertyInfo, List<UserControlRenderingPropertyAttribute>> result = new Dictionary<PropertyInfo, List<UserControlRenderingPropertyAttribute>>();
            PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty);
            foreach (PropertyInfo p in properties)
            {
                object[] oo = p.GetCustomAttributes(typeof(UserControlRenderingPropertyAttribute), true);
                if (oo.Length > 0)
                {
                    List<UserControlRenderingPropertyAttribute> list = new List<UserControlRenderingPropertyAttribute>();
                    list.Add(((UserControlRenderingPropertyAttribute)oo[0]));
                    result[p] = list;
                }
            }
            return result;
        }

        private static object GetDefaultValue(PropertyInfo property)
        {
            if (!s_defaultValueCache.ContainsKey(property))
            {
                lock (s_mutex)
                {
                    if (!s_defaultValueCache.ContainsKey(property))
                    {
                        object[] attributes = property.GetCustomAttributes(typeof(DefaultValueAttribute), true);
                        object value = attributes.Length > 0 ?
                            ((DefaultValueAttribute)attributes[0]).Value : null;
                        s_defaultValueCache[property] = value;
                    }
                }
            }

            return s_defaultValueCache[property];
        }

        public static void SetPropertyValues(UserControl control, HttpContext context)
        {
            Dictionary<
            PropertyInfo,
            List<UserControlRenderingPropertyAttribute>> metadata = GetMetadata(control.GetType());
            foreach (PropertyInfo property in metadata.Keys)
            {
                object value = GetValue(metadata[property], context) ?? GetDefaultValue(property);
                if (value != null)
                {
                    property.SetValue(control, Convert.ChangeType(value, property.PropertyType), null);
                }
            }
        }

        private static object GetValue(
            IEnumerable<UserControlRenderingPropertyAttribute> metadata,
            HttpContext context)
        {
            foreach (UserControlRenderingPropertyAttribute att in metadata)
            {
                NameValueCollection collection = (att.Source == UserControlRenderingPropertySource.QueryString) ?
                        context.Request.QueryString : context.Request.Form;
                object value = collection[att.Key];

                if (value != null) return value;
            }

            return null;
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}